package br.ufc.great.dataguide;

import java.io.Serializable;
import java.util.Vector;

import br.ufc.great.dtx.tm.lm.XDGLLockType;
import br.ufc.great.xml.XMLAttribute;
import br.ufc.great.xml.XMLDocument;
import br.ufc.great.xml.XMLElement;
import br.ufc.great.xml.XMLNode;

/**
 * 
 * @author Leonardo Oliveira Moreira
 *
 * Classe que representa um DataGuide
 */
public class DGDocument implements Serializable {

	private DGElement dgRoot;
	private long contId = -1;
	
	/**
	 * Construtor que recebe o número inicial dos identificadores do DataGuide e o document XML que irá ser sumarizado
	 * 
	 * @param startId
	 * @param document
	 */
	public DGDocument(XMLDocument document) {
		//XMLNode xmlRoot = document.getRoot();
		//Vector<XMLElement> buffer = new Vector<XMLElement>();
		//buffer.add(xmlRoot);
		//createDataGuide(buffer, dgRoot);
	}
	
	private void copyAttributesInElements(Vector<XMLElement> result, Vector<XMLAttribute> temp) {
		for (int i = 0; temp != null && i < temp.size(); i++)
			result.add(temp.elementAt(i));
	}

	private void copyNodesInElements(Vector<XMLElement> result, Vector<XMLNode> temp) {
		for (int i = 0; temp != null && i < temp.size(); i++)
			result.add(temp.elementAt(i));
	}

	private void createDataGuide(Vector<XMLElement> xmlElements, DGElement dgElement) {
		while (xmlElements.size() > 0) {
			Vector<XMLElement> buffer = new Vector<XMLElement>();
			XMLElement xmlCurrent = xmlElements.remove(0);
			DGElement dgCurrent = null;
			if (dgElement == null) {
				dgRoot = new DGElement(getNewId(), xmlCurrent.getName(), null);
				dgCurrent = dgRoot;
				dgCurrent.addIndex(xmlCurrent);				
			}
			else {
				dgCurrent = new DGElement(getNewId(), xmlCurrent.getName(), dgElement);
				dgCurrent.addIndex(xmlCurrent);
				dgElement.addChildren(dgCurrent);				
			}
			if (xmlCurrent instanceof XMLAttribute) {
				for (int i = 0; i < xmlElements.size(); i++) {
					XMLElement item = xmlElements.elementAt(i);
					if (item instanceof XMLAttribute && item.getName().equalsIgnoreCase(xmlCurrent.getName())) {
						dgCurrent.addIndex(item);
						xmlElements.removeElementAt(i);
						i--;
					}
				}
			}
			else {
				if (xmlCurrent instanceof XMLNode) {
					copyAttributesInElements(buffer, ((XMLNode) xmlCurrent).getAttributes());
					copyNodesInElements(buffer, ((XMLNode) xmlCurrent).getChildren());
					for (int i = 0; i < xmlElements.size(); i++) {
						XMLElement item = xmlElements.elementAt(i);
						if (item instanceof XMLNode && item.getName().equalsIgnoreCase(xmlCurrent.getName())) {
							dgCurrent.addIndex(item);
							xmlElements.removeElementAt(i);
							i--;
							copyAttributesInElements(buffer, ((XMLNode) item).getAttributes());
							copyNodesInElements(buffer, ((XMLNode) item).getChildren());
						}
					}
				}
			}
			createDataGuide(buffer, dgCurrent);
		}
	}

	public Vector<Vector<String>> getIndexTable() {
		Vector<Vector<String>> rows = new Vector<Vector<String>>();
		processElement(rows, getRoot());
		return rows;
	}
	
	public void processElement(Vector<Vector<String>> result, DGElement current) {
		Vector<String> row = new Vector<String>();
		row.add(String.valueOf(current.getId()));
		row.add(current.getName());
		String strIndexes = printIndexes(current.getIndexes());
		row.add(strIndexes);
		String strLocks = printLocks(current.getLockList());
		row.add(strLocks);
		result.add(row);
		Vector<DGElement> childrens = current.getChildrens();
		for (int i = 0; childrens != null && i < childrens.size(); i++)
			processElement(result, childrens.elementAt(i));
	}

	/**
	 * Método utilizado para transformar o DataGuide em um Documento XML no formato de String
	 */
	public String toString() {
		String strDataGuide = "";
		strDataGuide = printElement(dgRoot, 1);
		return strDataGuide;
	}

	/**
	 * Método utilizado para imprimir um elemento do DataGuide
	 * 
	 * @param current
	 * @param level
	 * @return
	 */
	private String printElement(DGElement current, int level) {
		String temp = "";
		for (int i = 0; level != 1 && i < (level * 2); i++)
			temp += " ";
		String strPointers = printIndexes(current.getIndexes());
		String strLocks = printLocks(current.getLockList());
		temp += "<" + current.getName() + " @id=\"" + current.getId() + "\" @pt=\"" + strPointers + "\" @lock=\"" + strLocks;
		Vector<DGElement> childrens = current.getChildrens();
		if (childrens != null && childrens.size() > 0) {
			temp += "\">\n";
			for (int i = 0; i < childrens.size(); i++) {
				DGElement child = childrens.elementAt(i);
				temp += printElement(child, level + 1);
			}
			for (int i = 0; level != 1 && i < (level * 2); i++)
				temp += " ";
			temp += "</" + current.getName()+ ">\n";			
		}
		else
			temp += "\" />\n";
		return temp;
	}

	/**
	 * Método utilizado para recuperar o próximo id de um elemento
	 * 
	 * @return
	 */
	public long getNewId() {
		return (++contId);
	}
	
	/**
	 * Método utilizado para resgatar o elemento raiz
	 * 
	 * @return
	 */
	public DGElement getRoot() {
		return dgRoot;
	}
	
	public void setRoot(DGElement newRoot) {
		dgRoot = newRoot;
	}

	private String printLocks(LockList lockList) {
		String strLocks = "";
		synchronized (lockList) {
			for (int i = 0; i < lockList.size(); i++) {
				LockItem item = lockList.get(i);
				//strLocks += item.getTransactionId() + "." + item.getOperationId() + ".";
				switch (item.getLockType()) {
					case XDGLLockType.EXCLUSIVE : {
						strLocks += "X";
						break;
					}
					case XDGLLockType.EXCLUSIVE_TREE : {
						strLocks += "XT";
						break;
					}
					case XDGLLockType.INTENTION_EXCLUSIVE : {
						strLocks += "IX";
						break;
					}
					case XDGLLockType.INTENTION_SHARED : {
						strLocks += "IS";
						break;
					}
					case XDGLLockType.SHARED : {
						strLocks += "S";
						break;
					}
					case XDGLLockType.SHARED_AFTER : {
						strLocks += "SA";
						break;
					}
					case XDGLLockType.SHARED_BEFORE : {
						strLocks += "SB";
						break;
					}
					case XDGLLockType.SHARED_INTO : {
						strLocks += "SI";
						break;
					}
					case XDGLLockType.SHARED_TREE : {
						strLocks += "ST";
						break;
					}
				}
				if (i < lockList.size() -  1)
					strLocks += ", ";
			}
		}
		return strLocks;
	}
	
	private String printIndexes(Vector<XMLElement> indexes) {
		String strPointers = "";
		for (int i = 0; indexes != null && i < indexes.size(); i++) {
			XMLElement index = indexes.get(i); 
			strPointers += index.getId();
			if (i < indexes.size() - 1)
				strPointers += ", ";
		}
		return strPointers;
	}
	
}
